From 61a0ace4b02a28977173feb66d082b2916a6d46a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 9 Apr 2015 14:35:48 -0700 Subject: [PATCH] Allow build script feedback to the crate compiled This commit enables the build script for a crate to provide feedback to the crate itself about how it should be built. This is done through the `--cfg` flags of the compiler, and each build script is now allowed to print `rustc-cfg` directives to inform Cargo about what `--cfg` flags it should pass. All `--cfg` flags are local to the current crate and are not propagated outwards to transitive dependencies. The primary use-case that this feature is targeting is compile-time feature detection for applications like C bindings or C libraries where the version being targeted may change over time. Closes #1478 --- src/cargo/ops/cargo_compile.rs | 3 ++ src/cargo/ops/cargo_rustc/custom_build.rs | 32 ++++++++------- src/cargo/ops/cargo_rustc/mod.rs | 11 ++++-- src/doc/build-script.md | 16 +++++--- tests/test_cargo_compile_custom_build.rs | 48 +++++++++++++++++++++++ 5 files changed, 87 insertions(+), 23 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 0ef49cdde..96fd5f4a1 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -379,6 +379,7 @@ fn scrape_target_config(config: &Config, triple: &str) let mut output = BuildOutput { library_paths: Vec::new(), library_links: Vec::new(), + cfgs: Vec::new(), metadata: Vec::new(), }; let key = format!("{}.{}", key, lib_name); @@ -406,6 +407,8 @@ fn scrape_target_config(config: &Config, triple: &str) output.library_paths.extend(a.into_iter().map(|v| { PathBuf::from(&v.0) })); + } else if k == "rustc-cfg" { + output.cfgs.extend(a.into_iter().map(|v| v.0)); } else { try!(config.expected("string", &k, ConfigValue::List(a, p))); diff --git a/src/cargo/ops/cargo_rustc/custom_build.rs b/src/cargo/ops/cargo_rustc/custom_build.rs index c64d3e49e..1f7d427cb 100644 --- a/src/cargo/ops/cargo_rustc/custom_build.rs +++ b/src/cargo/ops/cargo_rustc/custom_build.rs @@ -21,6 +21,8 @@ pub struct BuildOutput { pub library_paths: Vec, /// Names and link kinds of libraries, suitable for the `-l` flag pub library_links: Vec, + /// Various `--cfg` flags to pass to the compiler + pub cfgs: Vec, /// Metadata to pass to the immediate dependencies pub metadata: Vec<(String, String)>, } @@ -252,6 +254,7 @@ impl BuildOutput { pub fn parse(input: &str, pkg_name: &str) -> CargoResult { let mut library_paths = Vec::new(); let mut library_links = Vec::new(); + let mut cfgs = Vec::new(); let mut metadata = Vec::new(); let whence = format!("build script of `{}`", pkg_name); @@ -277,24 +280,25 @@ impl BuildOutput { whence, line))) }; - if key == "rustc-flags" { - let (libs, links) = try!( - BuildOutput::parse_rustc_flags(value, &whence) - ); - library_links.extend(links.into_iter()); - library_paths.extend(libs.into_iter()); - } else if key == "rustc-link-lib" { - library_links.push(value.to_string()); - } else if key == "rustc-link-search" { - library_paths.push(PathBuf::from(value)); - } else { - metadata.push((key.to_string(), value.to_string())) + match key { + "rustc-flags" => { + let (libs, links) = try!( + BuildOutput::parse_rustc_flags(value, &whence) + ); + library_links.extend(links.into_iter()); + library_paths.extend(libs.into_iter()); + } + "rustc-link-lib" => library_links.push(value.to_string()), + "rustc-link-search" => library_paths.push(PathBuf::from(value)), + "rustc-cfg" => cfgs.push(value.to_string()), + _ => metadata.push((key.to_string(), value.to_string())), } } Ok(BuildOutput { library_paths: library_paths, library_links: library_links, + cfgs: cfgs, metadata: metadata, }) } @@ -317,8 +321,8 @@ impl BuildOutput { } let value = match flags_iter.next() { Some(v) => v, - None => return Err(human(format!("Flag in rustc-flags has no value \ - in {}: `{}`", + None => return Err(human(format!("Flag in rustc-flags has no \ + value in {}: `{}`", whence, value))) }; match flag { diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 5617135d9..ac9f02a96 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -436,9 +436,14 @@ fn rustc(package: &Package, target: &Target, profile: &Profile, for path in output.library_paths.iter() { rustc.arg("-L").arg(path); } - if pass_l_flag && id == *current_id { - for name in output.library_links.iter() { - rustc.arg("-l").arg(name); + if id == *current_id { + for cfg in &output.cfgs { + rustc.arg("--cfg").arg(cfg); + } + if pass_l_flag { + for name in output.library_links.iter() { + rustc.arg("-l").arg(name); + } } } } diff --git a/src/doc/build-script.md b/src/doc/build-script.md index 61ff5e44d..31ff5ea43 100644 --- a/src/doc/build-script.md +++ b/src/doc/build-script.md @@ -79,18 +79,22 @@ Example output: ```notrust cargo:rustc-link-lib=static=foo cargo:rustc-link-search=native=/path/to/foo +cargo:rustc-cfg=foo cargo:root=/path/to/foo cargo:libdir=/path/to/foo/lib cargo:include=/path/to/foo/include ``` -The `rustc-link-lib` key indicates that Cargo should pass a `-l` option to -rustc. Similarly, `rustc-link-search` indicates that Cargo should pass a `-L` -option. +There are a few special keys that Cargo recognizes, affecting how the crate this +build script is for is built: -The `rustc-flags` key is special and indicates the flags that Cargo will -pass to Rustc. Currently only `-l` and `-L` are accepted. Using -`rustc-link-lib` and `rustc-link-search` is more robust. +* `rustc-link-lib` indicates that the specified value should be passed to the + compiler as a `-l` flag. +* `rustc-link-search` indicates the specified value should be passed to the + compiler as a `-L` flag. +* `rustc-cfg` indicates that the specified directive will be passed as a `--cfg` + flag to the compiler. This is often useful for performing compile-time + detection of various features. Any other element is a user-defined metadata that will be passed to dependencies. More information about this can be found in the [`links`][links] diff --git a/tests/test_cargo_compile_custom_build.rs b/tests/test_cargo_compile_custom_build.rs index d5fc4057a..35a1963a7 100644 --- a/tests/test_cargo_compile_custom_build.rs +++ b/tests/test_cargo_compile_custom_build.rs @@ -1252,3 +1252,51 @@ test!(test_duplicate_deps { assert_that(p.cargo_process("build"), execs().with_status(0)); }); + +test!(cfg_feedback { + let build = project("builder") + .file("Cargo.toml", r#" + [package] + name = "builder" + version = "0.0.1" + authors = [] + build = "build.rs" + "#) + .file("src/main.rs", " + #[cfg(foo)] + fn main() {} + ") + .file("build.rs", r#" + fn main() { + println!("cargo:rustc-cfg=foo"); + } + "#); + assert_that(build.cargo_process("build"), + execs().with_status(0)); +}); + +test!(cfg_override { + let (_, target) = ::cargo::ops::rustc_version().unwrap(); + + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.5.0" + authors = [] + links = "a" + build = "build.rs" + "#) + .file("src/main.rs", " + #[cfg(foo)] + fn main() {} + ") + .file("build.rs", "") + .file(".cargo/config", &format!(r#" + [target.{}.a] + rustc-cfg = ["foo"] + "#, target)); + + assert_that(p.cargo_process("build").arg("-v"), + execs().with_status(0)); +}); -- 2.30.2